#include <httpParser.h>
#include <regex>
#include <string.h>
#include <string>
namespace http {

    HttpParser::HttpParser() : content_length(0), part(StartLine) {
    }

    int HttpParser::parser(char *msg, size_t msg_size) {
        long ret = 0;
        buf.append(msg, msg_size);

        if (part < HeadersEnd) {
            std::regex regex_CRLF("\r\n");
            std::regex regex_and("&");
            auto       post = buf.find("\r\n\r\n");
            if (post == std::string::npos) {
                post == buf.find("\n\n");
                if (post == std::string::npos)
                    return 2048;
            }

            body.assign(buf.substr(post + 4));
            // buf.resize(post);

            std::sregex_token_iterator lines(buf.begin(), buf.end(), regex_CRLF, -1);
            decltype(lines)            lines_end;
            std::string                line;
            for (; lines != lines_end && part != HeadersEnd; lines++) {
                switch (part) {
                    case StartLine: {
                        std::istringstream line_stream(lines->str());
                        std::string        tmp;
                        line_stream >> tmp;
                        if (tmp.find("HTTP") == std::string::npos) {
                            request.http.insert(std::make_pair("method", tmp));
                            line_stream >> tmp;
                            // std::cout << tmp << std::endl;
                            if (tmp == "/")
                                tmp = "index.html";
                            else if (tmp[0] == '/')
                                tmp.erase(0, 1);

                            request.http.insert(std::make_pair("path", tmp));
                            line_stream >> tmp;
                            request.http.insert(std::make_pair("version", tmp));
                            if (request.http["method"] == "GET") {
                                std::string path = request.http["path"];
                                auto        post = path.find_first_of('?');
                                if (post != std::string::npos) {
                                    request.http["path"].assign(
                                        deescapeURL(
                                            path.substr(0, post)));
                                    request.body.assign(
                                        deescapeURL(
                                            path.substr(post + 1)));
                                }
                            }
                        }
                        else {
                            request.http.insert(std::make_pair("version", tmp));
                            line_stream >> tmp;
                            request.http.insert(std::make_pair("status", tmp));
                            line_stream >> tmp;
                            request.http.insert(std::make_pair("status_text", tmp));
                        }
                        part = Headers;
                        break;
                    }
                    case Headers: {
                        line = lines->str();
                        if (line.size() <= 1) {
                            ret  = 0;
                            part = HeadersEnd;
                            break;
                        }
                        auto pos = line.find(":");
                        if (pos == std::string::npos)
                            continue;
                        std::string tmp1(line, 0, pos);
                        std::string tmp2(line, pos + 2);
                        request.http.insert(std::make_pair(format_key(tmp1), tmp2));
                        break;
                    }
                    default: {
                        break;
                    }
                }
            }
        }
        if (request.http["method"] == "POST") {
            if (part >= HeadersEnd) {
                try {
                    if (part == Body) {
                        body.append(msg, msg_size);
                    }
                    if (part == HeadersEnd) {
                        part           = Body;
                        content_length = stoi(request.http.at("Content-Length"));
                        if (content_length <= 0) {
                            part = End;
                            return -411;
                        }
                    }
                    ret = content_length - body.size();
                    if (ret > 0) {
                        return ret;
                    }
                    part = End;
                    if (request.http["Content-Type"].find("multipart/form-data") == std::string::npos)
                        request.body.assign(body);
                    else
                        parserBody();
                }
                catch (const std::exception &e) {
                    std::cerr << e.what() << '\n';
                }
            }
        }
        else
            part == End;
        return ret;
    }

    HttpParser::~HttpParser() {}

    std::string HttpParser::operator[](std::string str) {
        return request[str];
    }

    std::string HttpParser::format_key(std::string &str) {
        if (str[0] >= 'a' && str[0] <= 'z') {
            str[0] = str[0] + 'A' - 'a';
        }
        int position = 0;
        while ((position = str.find("-", position)) != std::string::npos) {
            if (str[position + 1] >= 'a' && str[position + 1] <= 'z') {
                str[position + 1] = str[position + 1] + 'A' - 'a';
            }
            position++;
        }
        return str;
    }

    const void HttpParser::parserBody() {
        using namespace std;
        string boundary;
        string content_type = request.http.at("Content-Type");
        if (content_type.find("multipart/form-data") == string::npos) {
            body.assign(body.substr(0, content_length));
            return;
        }
        auto post = content_type.find("boundary");
        if (post != string::npos) {
            post = content_type.find_first_of("=", post);
            if (post != string::npos) {
                boundary.assign("--" + content_type.substr(post + 1));
            }
            post = body.find("Content-Disposition");
            if (post != string::npos) {
                post = body.find("filename", post);
                if (post != string::npos) {
                    post += strlen("filename=\"");
                    auto post_end = body.find_first_of('\"', post);
                    request.fileName.assign(body.substr(post, post_end - post));
                    post = post_end + 2;
                }
            }
            post = body.find("\r\n\r\n", post);

            if (post == string::npos) {
                cerr << "can't find CRLF" << endl;
            }
            else {
                post += 4;
                auto post_end = body.find(boundary, post) - 2;
                request.body.assign(body.substr(post, post_end - post));
                return;
            }
        }
    }

    char HttpParser::dec2hexChar(short int n) {
        if (0 <= n && n <= 9) {
            return char(short('0') + n);
        }
        else if (10 <= n && n <= 15) {
            return char(short('A') + n - 10);
        }
        else {
            return char(0);
        }
    }

    short int HttpParser::hexChar2dec(char c) {
        if ('0' <= c && c <= '9') {
            return short(c - '0');
        }
        else if ('a' <= c && c <= 'f') {
            return (short(c - 'a') + 10);
        }
        else if ('A' <= c && c <= 'F') {
            return (short(c - 'A') + 10);
        }
        else {
            return -1;
        }
    }

    std::string HttpParser::escapeURL(const std::string &URL) {
        std::string result = "";
        for (unsigned int i = 0; i < URL.size(); i++) {
            char c = URL[i];
            if (
                ('0' <= c && c <= '9') ||
                ('a' <= c && c <= 'z') ||
                ('A' <= c && c <= 'Z') ||
                c == '/' || c == '.' || c == '=' || c == '&' || c == '?') {
                result += c;
            }
            else {
                int j = (short int)c;
                if (j < 0) {
                    j += 256;
                }
                int i1, i0;
                i1 = j / 16;
                i0 = j - i1 * 16;
                result += '%';
                result += dec2hexChar(i1);
                result += dec2hexChar(i0);
            }
        }
        return result;
    }

    std::string HttpParser::deescapeURL(const std::string &URL) {
        std::string result = "";
        for (unsigned int i = 0; i < URL.size(); i++) {
            char c = URL[i];
            if (c != '%') {
                result += c;
            }
            else {
                char c1  = URL[++i];
                char c0  = URL[++i];
                int  num = 0;
                num += hexChar2dec(c1) * 16 + hexChar2dec(c0);
                result += char(num);
            }
        }
        return result;
    }
}  // namespace http
